/*
 * Copyright 2020 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp.instrumentation.reporter;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharStreams;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.javascript.jscomp.instrumentation.reporter.proto.InstrumentationPoint;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;

/**
 * This class will read a file that contains the instrumentation mapping generated by the compiler
 * production instrumentation pass, and also a list of other files which are the reports sent by the
 * instrumented production code. It will then take these inputs and generate a single JSON which
 * provides a detailed breakdown of each instrumentation point.
 */
@GwtIncompatible
final class ProductionInstrumentationReporter {

  @Option(
      name = "--mapping_file",
      usage = "The file name of the mapping generated by the production instrumentation pass.",
      required = true)
  private String instrumentationMappingLocation = "";

  @Option(
      name = "--reports_directory",
      usage =
          "The folder/directory which contains all the reports created by the instrumented"
              + " production code.",
      required = true)
  private String instrumentationReportsDirectory = "";

  @Option(
      name = "--result_output",
      usage =
          "Use this flag to provide the name of the final report that will be generated by"
              + "this reporter.",
      required = true)
  private String finalResultOutput = "";

  public static void main(String[] args) {
    try {
      new ProductionInstrumentationReporter().doMain(args);
    } catch (IOException e) {
      System.err.println(e.getMessage());
    }
  }

  /** This function reads a file at the given filePath and converts the contents into a string. */
  public static String readFile(String filePath) throws IOException {
    return CharStreams.toString(Files.newBufferedReader(Path.of(filePath), UTF_8));
  }

  /**
   * Reads all files found in folder and converts the contents of each file to a Map<String,
   * ProfilingData> data structure where it is a mapping of the unique param value to the encoded
   * values. The folder contains all the reports sent by the instrumented production code.
   */
  private ImmutableList<Map<String, Long>> getAllExecutionResults(File folder) throws IOException {
    ImmutableList.Builder<Map<String, Long>> result = ImmutableList.builder();

    for (final File fileEntry : folder.listFiles()) {
      String executionResult = readFile(fileEntry.getAbsolutePath());
      Type type = new TypeToken<Map<String, Long>>() {}.getType();
      Map<String, Long> executedInstrumentationData = new Gson().fromJson(executionResult, type);

      result.add(executedInstrumentationData);
    }

    return result.build();
  }

  /**
   * Creates a file with the given fileName (including extension) with the contents of the file
   * being provided by fileContents.
   */
  private void createFile(String fileName, String fileContents) throws IOException {

    File fold = new File(fileName);
    fold.delete();
    File myObj = new File(fileName);
    myObj.createNewFile();

    try (Writer myWriter = Files.newBufferedWriter(Path.of(fileName), UTF_8)) {
      myWriter.write(fileContents);
    }
  }

  private void doMain(String[] args) throws IOException {

    parseCmdLineArguments(args);

    Map<String, InstrumentationPoint> mapping =
        ReportDecoder.parseMappingFromFile(instrumentationMappingLocation);

    File folder = new File(instrumentationReportsDirectory);

    ImmutableList<Map<String, Long>> listOfExecutionResults = getAllExecutionResults(folder);

    createFile(
        finalResultOutput,
        ProfilingReport.createTabSeparatedProfilingReport(mapping, listOfExecutionResults));
  }

  private void parseCmdLineArguments(String[] args) {
    CmdLineParser parser = new CmdLineParser(this);
    parser.setUsageWidth(80);

    try {
      parser.parseArgument(args);
    } catch (CmdLineException e) {
      System.err.println(e.getMessage());
      return;
    }
  }
}
